package tree;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.CubicCurve2D;
import java.util.ArrayList;
import java.util.Random;

public class Tree {

	private ArrayList<Tree> branches;
	private ArrayList<TreePoint> points;
	int depth;
	private Vector2d p;
	private Vector2d dir;
	public double energy = 0;

	public Tree(int d) {
		p = new Vector2d(400, 400);
		dir = new Vector2d((Math.random() - 0.5), (Math.random() - 0.5));
		branches = new ArrayList<Tree>();
		points = new ArrayList<TreePoint>();
		depth = d;
		points.add(p.toPoint());
	}

	public Tree(int d, Vector2d point, Vector2d direction, double e, boolean a) {
		p = point;
		energy = e;
		dir = direction;
		branches = new ArrayList<Tree>();
		points = new ArrayList<TreePoint>();
		depth = d;
		TreePoint tp = p.toPoint();
		tp.energy = e;
		points.add(tp);
	}

	public Tree(int d, Vector2d point, Vector2d direction, double e) {
		p = point;
		energy = e;
		dir = direction;
		branches = new ArrayList<Tree>();
		points = new ArrayList<TreePoint>();
		depth = d;
		TreePoint tp = p.toPoint();
		tp.energy = e;
		points.add(tp);
		dir = dir.add(Vector2d.randomVector().mult(0.6));
		dir.Normalize();
	}

	public void generate(Random r) {

		// for (int i = 0; i < 2 + (5 - depth) * 15 + r.nextInt(20); i++) {

	}

	public boolean step(Random r) {
		if (energy < 0.5) {
			// System.out.println(energy);
			return false;

		}
		energy -= 0.2;
		// if(Math.random() > 0.99) length += 1000;

		for (Tree road : branches) {
			road.step(r);
		}
		// if (r.nextBoolean()) {
		dir = dir.add(Vector2d.randomVector().mult(0.2));

		// dir.y += ((10000 - energy * energy) / 50000) * r.nextDouble();
		// dir.y += ((100-(energy))/500);

		dir.Normalize();
		// }

		p = p.add(dir.mult(r.nextDouble() * 26 + 10));
		TreePoint tp = p.toPoint();
		tp.energy = energy;
		points.add(tp);
		if (r.nextDouble() > 0.3) {

			double split = Math.random() / 2;
			Tree road = new Tree(depth + 1, p, dir, energy * (split));
			energy *= (1 - split);
			branches.add(road);
		}
		return true;
	}

	public void draw(Graphics g) {
		for (Tree r : branches) {
			r.draw(g);
		}

		Color c = new Color(depth * 25, depth * 25, depth * 25);
		// c = Color.black;
		c = new Color(60, 40, 20);

		TreePoint last = null;
		TreePoint last2 = null;
		for (TreePoint p : points) {
			if (last != null) {
				g.setColor(c);
				Graphics2D g2 = (Graphics2D) g;
				float lwidth = (float) p.energy / 2;
				if (lwidth > 100)
					lwidth = 100;
				if (lwidth < 1)
					lwidth = 1;

				if (last2 == null)
					drawTaperedLine((int) last.x, (int) last.y, (int) last.x,
							(int) last.y, (int) p.x, (int) p.y,
							last.energy + 1, p.energy + 1, g);
				else
					drawTaperedLine((int) last2.x, (int) last2.y, (int) last.x,
							(int) last.y, (int) p.x, (int) p.y,
							last.energy + 1, p.energy + 1, g);
			}
			last2 = last;
			last = p;
		}
		// g.setColor(Color.green);
		// g.fillOval(last.x - 5,last.y - 5,10,10);
	}

	public void drawDebug(Graphics g) {

		for (Tree r : branches) {
			r.drawDebug(g);
		}
		TreePoint last = null;
		for (TreePoint p : points) {
			if (last != null) {
				g.setColor(Color.red);
				g.drawLine((int) last.x, (int) last.y, (int) p.x, (int) p.y);
				g.setColor(Color.green);
				g.fillOval((int) p.x - 1, (int) p.y - 1, 2, 2);
			}
			last = p;
		}
	}

	public static void drawTaperedLine(int xm, int ym, int x1, int y1, int x2,
			int y2, double startWidth, double endWidth, Graphics g) {

		int dX = x2 - x1;
		int dY = y2 - y1;

		int dX1 = x1 - xm;
		int dY1 = y1 - ym;
		// line length
		double lineLength = Math.sqrt(dX * dX + dY * dY);
		double lineLength1 = Math.sqrt(dX1 * dX1 + dY1 * dY1);
		double scale1 = (double) (startWidth) / (2 * lineLength1);
		double scale2 = (double) (endWidth) / (2 * lineLength);
		// The x,y increments from an endpoint needed to create a rectangle...
		double ddx1 = -scale1 * (double) dY1;
		double ddy1 = scale1 * (double) dX1;

		double ddx2 = -scale2 * (double) dY;
		double ddy2 = scale2 * (double) dX;

		ddx1 += (ddx1 > 0) ? 0.5 : -0.5;
		ddy1 += (ddy1 > 0) ? 0.5 : -0.5;

		ddx2 += (ddx1 > 0) ? 0.5 : -0.5;
		ddy2 += (ddy1 > 0) ? 0.5 : -0.5;

		int dx1 = (int) ddx1;
		int dy1 = (int) ddy1;

		int dx2 = (int) ddx2;
		int dy2 = (int) ddy2;

		// Now we can compute the corner points...
		int xPoints[] = new int[4];
		int yPoints[] = new int[4];

		xPoints[0] = x1 + dx1;
		yPoints[0] = y1 + dy1;
		xPoints[1] = x1 - dx1;
		yPoints[1] = y1 - dy1;
		xPoints[2] = x2 - dx2;
		yPoints[2] = y2 - dy2;
		xPoints[3] = x2 + dx2;
		yPoints[3] = y2 + dy2;

		g.fillPolygon(xPoints, yPoints, 4);
	}

}
